package com.handinfo.engine;

import java.io.DataInputStream;
import java.io.IOException;

import javax.microedition.lcdui.Graphics;

import com.handinfo.engine.resource.ObjectAdapter;
import com.handinfo.engine.resource.ResHead;
import com.handinfo.engine.resource.Resource;
import com.handinfo.engine.resource.ResourceManager;
import com.handinfo.engine.resource.Serializeable;
import com.handinfo.engine.resource.SerializeableImage;
import com.handinfo.util.Tools;


/**
 * 动画类 动画类封装了游戏中的动画效果，通常用于与元件绑定，并且由元件来管理动画状态，可以认 为是元件的脸面。不过也会经常用于一些菜单/界面特效上。
 * 
 * @author Xia Mingwei
 * @version 1.0
 */
public class Animation extends Serializeable
{
	public SerializeableImage[] m_images; // 图像对象
	public short[] m_imageWidth; // 图像宽度
	public short[] m_imageHeight; // 图像高度
	public boolean m_hasPalette = false; // 是否使用调色板
	public int m_paletteStart; // 调色板起始位置
	public byte[] m_paletteData; // 调色板数据
	public boolean m_hasFrameCollide; // 是否有帧碰撞信息

	/**
	 * 帧数据 1维长度=帧数; 2维长度=该帧的模块数 * 4 每个模块4个属性，分别为模块索引、翻转标识、X坐标、Y坐标
	 */
	public short[][] m_frames;

	/**
	 * 帧碰撞数据 1维长度=帧数； 2维长度=碰撞框信息
	 */
	short[][] m_frames_collideBox;
	short[][] m_frames_activeBox;

	/**
	 * 动作数据 1维长度=动作数; 2维长度=该动作的帧数 每帧1个short值存储数据，但分为两个属性，分别为帧索引、该帧持续帧数
	 * short的前6位是持续帧数，后10位为帧索引
	 */
	public short[][] m_actions;

	public static final int MODULE_PROP_COUNT = 5;
	public static final int MODULE_PROP_IMG = 0;
	public static final int MODULE_PROP_X = 1;
	public static final int MODULE_PROP_Y = 2;
	public static final int MODULE_PROP_W = 3;
	public static final int MODULE_PROP_H = 4;
	/**
	 * 所有module的集合 1维=module数; 2维=module信息(一共5个short)
	 */
	private short[][] m_modules;

	/**
	 * 动画对象构造器
	 */
	public Animation()
	{

	}

	/**
	 * 载入图片模块数据
	 * 
	 * @param dis
	 * @throws java.lang.Exception
	 */
	public void loadModule(DataInputStream dis) throws IOException
	{
		int count = dis.readShort(); // 模块数量

		m_modules = new short[count][MODULE_PROP_COUNT];
		for (int i = 0; i < count; i++) {
			m_modules[i][0] = dis.readShort();
			m_modules[i][1] = dis.readShort();
			m_modules[i][2] = dis.readShort();
			m_modules[i][3] = dis.readShort();
			m_modules[i][4] = dis.readShort();
		}
	}

	/**
	 * 载入帧数据
	 * 
	 * @param dis
	 * @throws java.lang.Exception
	 */
	public void loadFrame(DataInputStream dis) throws IOException
	{
		int count = dis.readShort(); // 帧数量
		m_frames = new short[count][];
		m_hasFrameCollide = (dis.readByte() != 0);
		if (m_hasFrameCollide) {
			m_frames_collideBox = new short[count][];
			m_frames_activeBox = new short[count][];
		}
		int count2;
		for (int i = 0; i < count; i++) {
			count2 = dis.readUnsignedByte(); // 该帧的模块数
			m_frames[i] = new short[count2 * 4];
			for (int ii = 0; ii < count2 * 4; ii += 4) {
				m_frames[i][ii] = (short) dis.readUnsignedShort();
				m_frames[i][ii + 1] = (short) dis.readUnsignedShort();
				m_frames[i][ii + 2] = (short) dis.readUnsignedShort();
				m_frames[i][ii + 3] = (short) dis.readUnsignedShort();
			}
			if (m_hasFrameCollide) {
				m_frames_collideBox[i] = new short[4];
				m_frames_activeBox[i] = new short[4];
				m_frames_collideBox[i][0] = dis.readShort();
				m_frames_collideBox[i][1] = dis.readShort();
				m_frames_collideBox[i][2] = dis.readShort();
				m_frames_collideBox[i][3] = dis.readShort();
				m_frames_activeBox[i][0] = dis.readShort();
				m_frames_activeBox[i][1] = dis.readShort();
				m_frames_activeBox[i][2] = dis.readShort();
				m_frames_activeBox[i][3] = dis.readShort();
			}
		}
	}

	/**
	 * 载入动作数据
	 * 
	 * @param dis
	 * @throws java.lang.Exception
	 */
	public void loadAction(DataInputStream dis) throws IOException
	{
		int count = dis.readShort(); // 动作数量
		m_actions = new short[count][];
		int data1;
		int data2;
		int count2;
		for (int i = 0; i < count; i++) {
			count2 = dis.readUnsignedByte(); // 该动作的帧数
			m_actions[i] = new short[count2];
			for (int j = 0; j < count2; j++) {
				// 这两个byte总共加起来16位，其中的十位用于记录帧索引，另外的6位记录帧持续时间
				data1 = dis.readUnsignedByte();
				data2 = dis.readUnsignedByte();
				m_actions[i][j] = (short) ((data2 << 8) + data1);
			}
		}
	}

	/**
	 * 载入图片
	 * 
	 * @param res
	 */
	public void loadImage(Resource res)
	{
		m_images = new SerializeableImage[m_refsResHead.length];
		m_imageWidth = new short[m_refsResHead.length];
		m_imageHeight = new short[m_refsResHead.length];
		for (int i = 0; i < m_refsResHead.length; i++) {
			m_images[i] = (SerializeableImage) (ResourceManager.getInstance()
					.loadResource(res, m_refsResHead[i]));
			if (m_images[i] != null) {
				m_imageWidth[i] = (short) m_images[i].m_image.getWidth();
				m_imageHeight[i] = (short) m_images[i].m_image.getHeight();
			}
		}
	}

	/**
	 * 释放资源
	 */
	public void dispose()
	{
		disposeHeader();
		if (m_frames != null) {
			for (int i = 0; i < m_frames.length; i++) {
				m_frames[i] = null;
			}
			m_frames = null;
		}
		if (m_actions != null) {
			for (int i = 0; i < m_actions.length; i++) {
				m_actions[i] = null;
			}
			m_actions = null;
		}
		if (m_modules != null) {
			for (int i = 0; i < m_modules.length; i++) {
				m_modules[i] = null;
			}
			m_modules = null;
		}
		if (m_images != null) {
			for (int i = 0; i < m_images.length; i++) {
				m_images[i] = null;
			}
			m_images = null;
		}
	}

	/**
	 * 销毁该Animation，包括其对应的图片
	 */
	public void destroy()
	{
		disposeHeader();
		if (m_frames != null) {
			for (int i = 0; i < m_frames.length; i++) {
				m_frames[i] = null;
			}
			m_frames = null;
		}
		if (m_actions != null) {
			for (int i = 0; i < m_actions.length; i++) {
				m_actions[i] = null;
			}
			m_actions = null;
		}
		if (m_modules != null) {
			for (int i = 0; i < m_modules.length; i++) {
				m_modules[i] = null;
			}
			m_modules = null;
		}
		if (m_images != null) {
			for (int i = 0; i < m_images.length; i++) {
				if (m_images[i] != null) {
					// 从res中移除依赖的图片
					if (!m_resource.isUseing(this, m_images[i].m_resHead)) {
						 m_resource.remove(m_images[i].m_resHead);
					}
					m_images[i] = null;
				}
			}
			m_images = null;
		}
	}

	/**
	 * 绘制
	 * 
	 * @param g
	 * @param x
	 * @param y
	 * @param flipX
	 * @param flipY
	 * @param frameIndex
	 */
	public void draw(Graphics g, int x, int y, boolean flipX, boolean flipY, int frameIndex)
	{
		if (!m_loadFinished){
			return;
		}
			
    	short[] frame = null;
    	try {
       		frame = m_frames[frameIndex];
		}
		catch (Exception e) {
			Tools.debugPrintln("BUG Animation="+this.m_resHead.m_url);
		}

   		int imgId;
//   		boolean isAvatar, isMain;
   		int modID, flag, posX, posY;
   		short modX, modY, modW, modH;
   		boolean flipX_real, flipY_real;
   		for (int i = 0; i < frame.length; i += 4) {
   			modID = (frame[i] & 0xFFFF);
   			flag = frame[i + 1] & 0xFFFF;

   			imgId = m_modules[modID][MODULE_PROP_IMG];
   			modX = m_modules[modID][MODULE_PROP_X];
   			modY = m_modules[modID][MODULE_PROP_Y];
   			modW = m_modules[modID][MODULE_PROP_W];
   			modH = m_modules[modID][MODULE_PROP_H];

   			if (modX + modW > m_imageWidth[imgId]) {
   				modW = (short) (m_imageWidth[imgId] - modX);
   			}
   			if (modY + modH > m_imageHeight[imgId]) {
   				modH = (short) (m_imageHeight[imgId] - modY);
   			}
   			if (modW <= 0 || modH <= 0) {
   				continue;
   			}

   			flipX_real = flipX ^ ((flag & 0x01) != 0);
   			flipY_real = flipY ^ ((flag & 0x02) != 0);

   			if (flipX_real) {
   				flag |= 0x01;
   			}
   			else {
   				flag &= 0xfffffffe;
   			}
   			if (flipY_real) {
   				flag |= 0x02;
   			}
   			else {
   				flag &= 0xfffffffd;
   			}

   			posX = x;
   			posY = y;
   			int clipW = modW, clipH = modH;
   			switch (flag) {
   				case 1:
   					posX -= modW;
   					break;
   				case 2:
   					posY -= modH;
   					break;
   				case 3:
   					posX -= modW;
   					posY -= modH;
   					break;
   				case 4:
   					posX -= modH;
   					clipW = modH;
   					clipH = modW;
   					break;
   				case 5:
   					clipW = modH;
   					clipH = modW;
   					break;
   				case 6:
   					posX -= modH;
   					posY -= modW;
   					clipW = modH;
   					clipH = modW;
   					break;
   				case 7:
   					posY -= modW;
   					clipW = modH;
   					clipH = modW;
   					break;
   				default:
   					break;
   			}

   			flag = AbstractGameScreen.trans(flag);

   			if (flipX) {
   				posX -= frame[i + 2];
   			}
   			else {
   				posX += frame[i + 2];
   			}
   			if (flipY) {
   				posY -= frame[i + 3];
   			}
   			else {
   				posY += frame[i + 3];
   			}

   			if (posX + modW < 0 || posX >= AbstractGameScreen.SCREEN_WIDTH) {
   				continue;
   			}
   			if (posY + modH < 0 || posY >= AbstractGameScreen.SCREEN_HEIGHT) {
   				continue;
   			}

   			g.setClip(posX, posY, clipW, clipH);
   			g.clipRect(posX, posY, clipW, clipH);
   			if (m_images[imgId].m_image == null)
   				return;
   			if (flag == 0) {
   				g.drawImage(m_images[imgId].m_image, posX - modX, posY - modY, Graphics.LEFT | Graphics.TOP);
   			}
   			else {
   				AbstractGameScreen.drawRegion(g, m_images[imgId].m_image, modX, modY, modW, modH, flag, posX, posY, 0);
   			}
   		}
   		g.setClip(0, 0, AbstractGameScreen.SCREEN_WIDTH, AbstractGameScreen.SCREEN_HEIGHT);
   		g.clipRect(0, 0, AbstractGameScreen.SCREEN_WIDTH, AbstractGameScreen.SCREEN_HEIGHT);
	}
	
	public void draw(Graphics g, int x, int y, boolean flipX, boolean flipY, int actionId, int frameId)
	{
		if (!m_loadFinished)
			return;
    	short[] frame = null;
    	int frameIndex = m_actions[actionId][frameId] & 0x03FF;
       	frame = m_frames[frameIndex];
   		int imgId;
//   		boolean isAvatar, isMain;
   		int modID, flag, posX, posY;
   		short modX, modY, modW, modH;
   		boolean flipX_real, flipY_real;
   		for (int i = 0; i < frame.length; i += 4) {
   			modID = (frame[i] & 0xFFFF);
   			flag = frame[i + 1] & 0xFFFF;

   			imgId = m_modules[modID][MODULE_PROP_IMG];
   			modX = m_modules[modID][MODULE_PROP_X];
   			modY = m_modules[modID][MODULE_PROP_Y];
   			modW = m_modules[modID][MODULE_PROP_W];
   			modH = m_modules[modID][MODULE_PROP_H];

   			if (modX + modW > m_imageWidth[imgId]) {
   				modW = (short) (m_imageWidth[imgId] - modX);
   			}
   			if (modY + modH > m_imageHeight[imgId]) {
   				modH = (short) (m_imageHeight[imgId] - modY);
   			}
   			if (modW <= 0 || modH <= 0) {
   				continue;
   			}

   			flipX_real = flipX ^ ((flag & 0x01) != 0);
   			flipY_real = flipY ^ ((flag & 0x02) != 0);

   			if (flipX_real) {
   				flag |= 0x01;
   			}
   			else {
   				flag &= 0xfffffffe;
   			}
   			if (flipY_real) {
   				flag |= 0x02;
   			}
   			else {
   				flag &= 0xfffffffd;
   			}

   			posX = x;
   			posY = y;
   			int clipW = modW, clipH = modH;
   			switch (flag) {
   				case 1:
   					posX -= modW;
   					break;
   				case 2:
   					posY -= modH;
   					break;
   				case 3:
   					posX -= modW;
   					posY -= modH;
   					break;
   				case 4:
   					posX -= modH;
   					clipW = modH;
   					clipH = modW;
   					break;
   				case 5:
   					clipW = modH;
   					clipH = modW;
   					break;
   				case 6:
   					posX -= modH;
   					posY -= modW;
   					clipW = modH;
   					clipH = modW;
   					break;
   				case 7:
   					posY -= modW;
   					clipW = modH;
   					clipH = modW;
   					break;
   				default:
   					break;
   			}

   			flag = AbstractGameScreen.trans(flag);

   			if (flipX) {
   				posX -= frame[i + 2];
   			}
   			else {
   				posX += frame[i + 2];
   			}
   			if (flipY) {
   				posY -= frame[i + 3];
   			}
   			else {
   				posY += frame[i + 3];
   			}

   			if (posX + modW < 0 || posX >= AbstractGameScreen.SCREEN_WIDTH) {
   				continue;
   			}
   			if (posY + modH < 0 || posY >= AbstractGameScreen.SCREEN_HEIGHT) {
   				continue;
   			}

   			g.setClip(posX, posY, clipW, clipH);
   			g.clipRect(posX, posY, clipW, clipH);
   			if (m_images[imgId].m_image == null)
   				return;
   			if (flag == 0) {
   				g.drawImage(m_images[imgId].m_image, posX - modX, posY - modY, Graphics.LEFT | Graphics.TOP);
   			}
   			else {
   				AbstractGameScreen.drawRegion(g, m_images[imgId].m_image, modX, modY, modW, modH, flag, posX, posY, 0);
   			}
   		}
   		g.setClip(0, 0, AbstractGameScreen.SCREEN_WIDTH, AbstractGameScreen.SCREEN_HEIGHT);
   		g.clipRect(0, 0, AbstractGameScreen.SCREEN_WIDTH, AbstractGameScreen.SCREEN_HEIGHT);
	}

	public void read(Resource res, DataInputStream dis) throws IOException
	{
		readHeader(res, dis);
		// 载入模块数据
		loadModule(dis);
		// 载入所有帧数据
		loadFrame(dis);
		// 载入所有动作数据
		loadAction(dis);
		// 载入图片
		loadImage(res);

		checkLoadFinished();
		// //调色板数据
		// m_hasPalette = dis.readBoolean();
		// if (m_hasPalette) {
		// m_paletteStart = dis.readShort();
		// m_paletteData = new byte[dis.readShort()];
		// dis.read(m_paletteData);
		// }
	}

	public void checkLoadFinished()
	{
		if (m_images == null) {
			m_loadFinished = false;
			return;
		}
		for (int i = 0; i < m_images.length; i++) {
			if (m_images[i] == null) {
				m_loadFinished = false;
				return;
			}
		}
		m_loadFinished = true;
	}

	public synchronized void updateComponent(ResHead head,
			Serializeable component)
	{
		if (m_loadFinished)
			return;
		if (head.m_type != ObjectAdapter.RESOURCE_TYPE_PNG)
			return;
		if (m_refsResHead == null || m_refsResHead.length == 0)
			return;
		for (int i = 0; i < m_refsResHead.length; i++) {
			if (m_refsResHead[i].equals(head)) {
				m_images[i] = (SerializeableImage) component;
				m_imageWidth[i] = (short) m_images[i].m_image.getWidth();
				m_imageHeight[i] = (short) m_images[i].m_image.getHeight();
				checkLoadFinished();
			}
		}
	}

	public boolean isContainHeader(ResHead head)
	{
		if (head.m_type != ObjectAdapter.RESOURCE_TYPE_PNG)
			return false;
		if (m_refsResHead == null || m_refsResHead.length == 0)
			return false;
		for (int i = 0; i < m_refsResHead.length; i++) {
			if (m_refsResHead[i].equals(head)) {
				return true;
			}
		}
		return false;
	}
}
